package com.cyj.dream.gateway.handler;

import cn.hutool.core.io.resource.ResourceUtil;
import com.cyj.dream.captcha.callback.CacheCallback;
import com.cyj.dream.captcha.core.CaptchaVo;
import com.cyj.dream.captcha.core.PuzzleCaptcha;
import com.cyj.dream.captcha.model.ArithmeticCaptcha;
import com.cyj.dream.captcha.util.CaptchaUtil;
import com.cyj.dream.core.constant.CacheConstants;
import com.cyj.dream.core.constant.SecurityConstants;
import com.cyj.dream.gateway.config.GatewayConfigProperties;
import com.cyj.dream.gateway.constant.ImageCodeConstant;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import java.awt.*;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Description: 验证码生成处理器
 * @BelongsProject: DreamChardonnay
 * @BelongsPackage: com.cyj.dream.gateway.handler
 * @Author: ChenYongJia
 * @CreateTime: 2021-09-27 12:44
 * @Email: chen87647213@163.com
 * @Version: 1.0
 */
@Slf4j
@RequiredArgsConstructor
public class ImageCodeHandler implements HandlerFunction<ServerResponse> {

    private static final Integer DEFAULT_IMAGE_WIDTH = 100;

    private static final Integer DEFAULT_IMAGE_HEIGHT = 40;

    private final RedisTemplate redisTemplate;

    private final GatewayConfigProperties gatewayConfigProperties;


    @Override
    public Mono<ServerResponse> handle(ServerRequest serverRequest) {
        String type = "";
        if (serverRequest.queryParam("type").isPresent()) {
            type = serverRequest.queryParam("type").get();
        }
        //拖拽验证码模式
        if (ImageCodeConstant.IMAGE_TYPE_DRAG.equals(type)) {
            String randomStr = serverRequest.queryParam("randomStr").get();
            //设置源图片
            PuzzleCaptcha puzzleCaptcha = new PuzzleCaptcha(ResourceUtil.getStream("images/captcha/default.jpg"));
            //设置画质
            puzzleCaptcha.setImageQuality(Image.SCALE_AREA_AVERAGING);
            //执行分割图片
            puzzleCaptcha.run();
            //获取到验证码图片
            CaptchaVo captcha = CaptchaUtil.captcha(puzzleCaptcha, randomStr, this.getCacheCallback());
            return ServerResponse.status(HttpStatus.OK).contentType(MediaType.IMAGE_JPEG)
                    .body(captcha, CaptchaVo.class);
        }
        //默认输入验证
        else {
            ArithmeticCaptcha captcha = new ArithmeticCaptcha(DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT);

            String result = captcha.text();

            // 保存验证码信息
            String randomStr = serverRequest.queryParam("randomStr").get();
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.opsForValue().set(CacheConstants.DEFAULT_CODE_KEY + randomStr, result,
                    SecurityConstants.CODE_TIME, TimeUnit.SECONDS);

            // 转换流信息写出
            FastByteArrayOutputStream os = new FastByteArrayOutputStream();
            captcha.out(os);

            return ServerResponse.status(HttpStatus.OK).contentType(MediaType.IMAGE_JPEG)
                    .body(BodyInserters.fromResource(new ByteArrayResource(os.toByteArray())));
        }
    }

    private CacheCallback getCacheCallback() {
        return new CacheCallback() {
            @Override
            public void removeCache(String cacheKey) {
                redisTemplate.delete(cacheKey);
            }

            @Override
            public void putCache(String cacheKey, Object value, long exp, TimeUnit timeUnit) {
                redisTemplate.opsForValue().set(cacheKey, value,
                        SecurityConstants.CODE_TIME, TimeUnit.SECONDS);
            }

            @Override
            public Map<String, Object> getCache(String cacheKey) {
                Object o = redisTemplate.opsForValue().get(cacheKey);
                if (o instanceof Map) {
                    return (Map) o;
                }
                return null;
            }
        };
    }

}
